
#include "opend.h"
#include <poll.h>

#define NALLOC 10

static struct pollfd *grow_pollfd(struct pollfd *pfd, int *maxfd)
{
    int oldmax = *maxfd;
    int newmax = oldmax + NALLOC;

    if (NULL == (pfd = realloc(pfd, newmax*sizeof(struct pollfd))))
    {
        err_sys("realloc error");
    }

    for (int i = oldmax; i < newmax; i++)
    {
        pfd[i].fd = -1;
        pfd[i].events = POLLIN;
        pfd[i].revents = 0;
    }

    *maxfd = newmax;
    return pfd;
}

void loop(void)
{
    int listenfd, clifd, nread;
    char buf[MAXLINE];
    uid_t uid;
    struct pollfd *pollfd;
    int numfd = 1;
    int maxfd = NALLOC;

    if (NULL == (pollfd = malloc(NALLOC*sizeof(struct pollfd))))
    {
        err_sys("malloc error");
    }

    for (int i = 0; i < NALLOC; i++)
    {
        pollfd[i].fd = -1;
        pollfd[i].events = POLLIN;
        pollfd[i].revents = 0;
    }

    // obtain fd to listen for client request on
    if ((listenfd = serv_listen(CS_OPEN)) < 0)
    {
        log_sys("serv_listen error");
    }
    client_add(listenfd, 0);
    pollfd[0].fd = listenfd;

    while (1)
    {
        if (poll(pollfd, numfd, -1) < 0)
        {
            log_sys("poll error");
        }

        if (POLLIN & pollfd[0].revents)
        {
            if ((clifd = serv_accept(listenfd, &uid)) < 0)
            {
                log_sys("serv_accept error");
            }
            client_add(clifd, uid);

            // possibly increase the size of the pollfd array
            if (numfd == maxfd)
            {
                pollfd = grow_pollfd(pollfd, &maxfd);
            }
            pollfd[numfd].fd = clifd;
            pollfd[numfd].events = POLLIN;
            pollfd[numfd].revents = 0;
            numfd++;
            log_msg("new connection: uid %d, fd %d", uid, clifd);
        }

        for (int i = 1; i < numfd; i++)
        {
            if (POLLHUP & pollfd[i].revents)
            {
                goto hungup;
            }
            else if (POLLIN & pollfd[i].revents)
            {
                if ((nread = read(pollfd[i].fd, buf, MAXLINE)) < 0)
                {
                    log_sys("read error on fd %d", pollfd[i].fd);
                }
                else if (0 == nread)
                {
hungup:
                    // the client closed the connection
                    log_msg("closed: uid %d, fd %d", client[i].uid, pollfd[i].fd);
                    client_del(pollfd[i].fd);
                    close(pollfd[i].fd);

                    if (i < (numfd - 1))
                    {
                        // pack the array
                        pollfd[i].fd = pollfd[numfd-1].fd;
                        pollfd[i].events = pollfd[numfd-1].events;
                        pollfd[i].revents = pollfd[numfd-1].revents;
                        i--;
                    }
                    numfd--;
                }
                else
                {
                    handle_request(buf, nread, pollfd[i].fd, client[i].uid);
                }
            }
        }
    }
}
